/*
 * Copyright 2013-2017 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package org.springframework.cloud.gateway.filter.factory;

import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.cloud.gateway.filter.ratelimit.RateLimiter;
import org.springframework.cloud.gateway.filter.ratelimit.RedisRateLimiter;
import org.springframework.http.HttpStatus;
import org.springframework.tuple.Tuple;

import java.util.Arrays;
import java.util.List;

/**
 * User Request Rate Limiter filter. See https://stripe.com/blog/rate-limiters and
 */
public class RequestRateLimiterGatewayFilterFactory implements GatewayFilterFactory {

	public static final String KEY_RESOLVER_KEY = "keyResolver";

	/**限流器。默认情况下，使用RedisRateLimiter
	 * 
	 */
	private final RateLimiter rateLimiter;
	/**限流键解析器。默认情况下，使用PrincipalNameKeyResolver
	 * 
	 */
	private final KeyResolver defaultKeyResolver;

	public RequestRateLimiterGatewayFilterFactory(RateLimiter rateLimiter, KeyResolver defaultKeyResolver) {
		this.rateLimiter = rateLimiter;
		this.defaultKeyResolver = defaultKeyResolver;
	}

    @Override
    // 定义了Tuple参数的Key为replenishRate/burstCapacity/keyResolver
    public List<String> argNames() {
        return Arrays.asList(
                RedisRateLimiter.REPLENISH_RATE_KEY,
                RedisRateLimiter.BURST_CAPACITY_KEY,
                KEY_RESOLVER_KEY
        );
    }

    @Override
    public boolean validateArgs() {
        return false;
    }

    @SuppressWarnings("unchecked")
	@Override
	// 创建RequestRateLimiterGatewayFilter对象
	public GatewayFilter apply(Tuple args) {
    	// 校验Tuple参数至少有两个元素，即replenishRate和burstCapacity。而keyResolver是选填，为空时，使用默认值defaultKeyResolver
        validateMin(2, args);

        // 获得keyResolver。通过它，获得请求的限流键，例如URL / 用户 / IP等
		KeyResolver keyResolver;
		if (args.hasFieldName(KEY_RESOLVER_KEY)) {
			keyResolver = args.getValue(KEY_RESOLVER_KEY, KeyResolver.class);
		} else {
			keyResolver = defaultKeyResolver;
		}

		// 创建RequestRateLimiterGatewayFilter对象并返回
		// 调用KeyResolver#resolve(ServerWebExchange)方法，获得请求的限流键，
		// 注意，这里未处理限流键为空的情况（TODO: if key is empty?）。所以，当限流键为空时，过滤器链不会继续向下执行，也就是说，不会请求后端Http/Websocket服务，并且最终返回客户端200状态码，内容为空
		return (exchange, chain) -> keyResolver.resolve(exchange).flatMap(key ->
            // TODO: if key is empty?
			// 调用RateLimiter#isAllowed(ServerWebExchange, Tuple)方法，判断是否被限流
			rateLimiter.isAllowed(key, args).flatMap(response -> {
                // TODO: set some headers for rate, tokens left

                // 未被限流，允许访问，提交过滤器链继续过滤
                if (response.isAllowed()) {
                    return chain.filter(exchange);
                }

                // 被限流，不允许访问，设置响应429状态码，并回写客户端响应（exchange.getResponse().setComplete()）
                exchange.getResponse().setStatusCode(HttpStatus.TOO_MANY_REQUESTS);
                return exchange.getResponse().setComplete();
            }));
	}

}
